
#ifndef __M_CUTIL_H__
#define __M_CUTIL_H__

#include<iostream>
#include<string>
#include<vector>
#include<fstream>
#include<sstream>
#include<memory>
#include<experimental/filesystem>   //c++自己的文件系统
#include<sys/stat.h>
#include<jsoncpp/json/json.h>
#include"Clog.hpp"
#include"bundle.h"


namespace Hxy_Cloud
{
    namespace fs = std::experimental::filesystem;   //对文件系统里面的命名空间重命名
    class FileUtil
    {
        public:
            FileUtil(const std::string& name):_filename(name){}
            //使用系统接口stat可以获取文件的属性，包括修改时间和大小等
            //获取文件最近访问时间
            time_t LastATime()
            {
                struct stat st; 
                stat(_filename.c_str(),&st);  //文件属性放入struct stat中
                return st.st_atim.tv_sec;
            }
            //获取文件最近修改时间
            time_t LastMTime()
            {
                struct stat st; 
                stat(_filename.c_str(),&st);  //文件属性放入struct stat中
                return st.st_mtim.tv_sec;
            }
            //获取文件大小
            size_t FileSize()
            {
                struct stat st; 
                stat(_filename.c_str(),&st);  //文件属性放入struct stat中
                return st.st_size;
            }

            //获取文件路径里的文件名称
            std::string FileName()
            {
                //获取一串字符串目录下的文件名 -- ./abc/abc/text.txt -> text.txt
                int pos = _filename.find_last_of("/");  //找到最后一个/的位置
                if(pos == std::string::npos)
                {
                    //没找到目录 -- 表面在当前目录下 传入的是一个文件名没有路径
                    //CloudLog::GetInstance().Error("_filename路径下未能找到文件\n");
                    return _filename;
                }
                return _filename.substr(pos+1);
            }

            //向文件内写入内容
            bool SetContent(const std::string& body)
            {
                //打开文件
                std::ofstream ofs(_filename,std::ios::binary);
                if(ofs.is_open() == false)
                {
                    CloudLog::GetInstance().Error(__FILE__,__LINE__,"文件打开失败");
                    return false;
                }
                //把body所有内容写入文件
                ofs.write(body.c_str(),body.size());
                //关闭文件
                ofs.close();
                return true;
            }
            //读取文件内容
            bool GetContent(std::string* body)
            {
                //打开文件
                std::ifstream ifs(_filename.c_str(),std::ios::binary);
                if(ifs.is_open() == false)
                {
                    CloudLog::GetInstance().Error(__FILE__,__LINE__,"文件打开失败");
                    return false;
                }
                //获取文件大小，初始化body,把文件信息写入body
                ifs.seekg(std::ios::beg);
                size_t n = FileSize();
                body->resize(n);
                ifs.read(&(*body)[0],n);
                //关闭文件
                ifs.close();
                return true;
            }
            //获得文件指定位置指定长度的内容，用于实现文件的断点续传功能
            bool GetPoslen(std::string* content,size_t pos,size_t len)
            {
                //打开文件
                std::ifstream ifs(_filename.c_str(),std::ios::binary);
                if(ifs.is_open() == false)
                {
                    CloudLog::GetInstance().Error(__FILE__,__LINE__,"文件打开失败");
                    return false;
                }
                if(pos+len > FileSize())
                {
                    CloudLog::GetInstance().Error(__FILE__,__LINE__,"传入的pos和len大于文件大小");
                    return false;
                }
                //将文件下标走到指定位置 然后读取len长度字符
                ifs.seekg(pos);
                content->resize(len);
                ifs.read(&(*content)[0],len);
                //关闭文件
                ifs.close();
                return true;
            }
            //压缩文件 -- 传入压缩包的名称
            bool Compress(const std::string& packname)
            {
                //提取当前文件内容
                std::string content;
                if(GetContent(&content)==false)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"获得文件内容失败");
                    return false;
                }
                //将内容压缩
                std::string newcontent = bundle::pack(bundle::LZIP,content);
                //将压缩好的内容放进packname文件
                FileUtil fu(packname);
                if(fu.SetContent(newcontent) == false)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"文件输入失败");
                    return false;
                }
                return true;
            }
            //解压文件 -- 传入需要将数据解压到新文件的文件名
            bool UnCompress(const std::string& filename)
            {
                //提取当前文件内容
                std::string content;
                if(GetContent(&content)==false)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"获得文件内容失败");
                    return false;
                }
                //将内容压缩
                std::string newcontent = bundle::unpack(content);
                //将压缩好的内容放进packname文件
                FileUtil fu(filename);
                if(fu.SetContent(newcontent) == false)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"文件输入失败");
                    return false;
                }
                return true;
            }
            //判断文件是否存在
            bool Exists()
            {
                return fs::exists(_filename);
            }
            //创建文件目录
            bool CreateDirectory()
            {
                if(Exists()) return true;
                return fs::create_directories(_filename);
            }
            //获取文件目录
            bool GetDirectorys(std::vector<std::string>* arry)
            {
                for(auto& name : fs::directory_iterator(_filename))
                {
                    //如果是文件名是一个目录则跳过
                    if(fs::is_directory(name) == true)
                        continue;
                    //不是目录则把文件名并带路径插入arry
                    //relative_path 能狗够获取带有路径的文件名
                    arry->push_back(fs::path(name).relative_path().string());
                }
                return true;
            }
        private:
            std::string _filename; //文件名称
    };

    class JsonUtil
    {
        public:
            //传入value对象 返回序列化好的字符串
            static bool Serializable(const Json::Value& val,std::string* ser)
            {
                Json::StreamWriterBuilder build;
                std::unique_ptr<Json::StreamWriter> writer(build.newStreamWriter());
                std::stringstream ss;
                if(writer->write(val,&ss) != 0)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"序列化失败！");
                    return false;
                }
                *ser = ss.str();
                return true;
            }
            //传入序列化好的字符串 返回一个反序列化好的value
            static bool Deserialization(const std::string& deser,Json::Value* val)
            {
                Json::CharReaderBuilder build;
                std::unique_ptr<Json::CharReader> reader(build.newCharReader());
                std::string err;
                bool ret = reader->parse(deser.c_str(),deser.c_str()+deser.size(),val,&err);
                if(ret == false)
                {
                    CloudLog::GetInstance().Fatal(__FILE__,__LINE__,"反序列化失败 : %s",err.c_str());
                    return false;
                }
                return true;
            }
    };

}
#endif